在动态库中如何调用外部函数(注册回调函数) |
您所在的位置:网站首页 › 未定义function handle › 在动态库中如何调用外部函数(注册回调函数) |
一、背景介绍
在程序开发过程中,动态库可以需要调用其它外部模块中的函数,以便进行数据交互,减少代码重复等功能 本章解决的问题是:动态库在执行过程中如何到其它外部模块里调用函数? 在调用过程中需要考虑一个问题,外部模块的函数在哪里呢?怎么才能找到它藏在内存的哪个地址? 解决办法:其它外部模块向动态库注册回调函数! 二、程序测试 2.1 动态库 2.1.1 源码 // g++ lib.cpp -fPIC -shared -o lib.so -lrt -lpthread #include #include #include typedef void (*pf_void)(void); // 默认实现,必须有,否则线程创建会报错 extern "C" void func_in_main_def(void){ printf("the main is lazy, do NOT register me! \n"); } // 定义外部函数指针,需先定义函数再定义指针 pf_void func_in_main = func_in_main_def; extern "C" void mytimer(void){ int cnt =0; while (1) { printf("lib is runing %d !\n",cnt++); func_in_main(); std::this_thread::sleep_for(std::chrono::milliseconds(1000)); } } // 需先定义mytimer函数,再创建mytimer线程 std::thread sub_thread(mytimer); extern "C" void register_func(pf_void pf){ func_in_main = pf; } extern "C" int func_in_lib(int k){ printf("func_in_lib is called \n"); if (func_in_main) func_in_main(); return k + 1; } 2.1.2 编译编译: g++ lib.cpp -fPIC -shared -o lib.so -lrt -lpthread 2.2 外部模块 2.2.1 源码 // g++ registermain.cpp -ldl -o registermain #include #include #include #include #include #include typedef void (*pf_void)(void); typedef int (*pfunc)(int); typedef int (*pregister)(pf_void); void func_in_main(void){ printf("func_in_main \n"); } int main(int argc, char *agv[]){ int a = 1; int b; // 打开动态库 printf("press key to pass! \n"); void *handle = dlopen("./lib.so", RTLD_NOW); // char ch =getchar(); // 测试动态库中子线程运行时序,在加载动态库即开始运行子线程 if (handle){ // 查找动态库中的注册函数 pregister register_func = (pregister) dlsym(handle, "register_func"); if (register_func){ register_func(func_in_main); } // 查找动态库中的函数 pfunc func = (pfunc) dlsym(handle, "func_in_lib"); if (func){ b = func(a); printf("b = %d \n", b); } else{ printf("dlsym failed! \n"); } } else{ printf("dlopen failed! \n"); } while (1){ printf("main is runing! \n"); std::this_thread::sleep_for(std::chrono::milliseconds(1000)); } dlclose(handle); return 0; } 2.2.2 编译与运行编译: g++ registermain.cpp -ldl -o registermain 运行: ./registermain 输出: func_in_lib is called func_in_main b = 2 main is runing! lib is runing 0 ! func_in_main main is runing! lib is runing 1 ! 三、代码解释 3.1 流程图名词解释: 注册函数:注册函数register_func()在动态库中定义,在外部模块中使用。 回调函数:被动态库调用的外部模块中的函数,此处是func_in_main。 注册回调函数:一种行为,程序通过dlopen加载动态库,将自己的函数向动态库注册,让动态库可以调用自己的函数;有时也指注册函数。整个程序运行过程中,外部模块与动态库的交互流程如下图: 其中粉色为外部模块执行代码,绿色为动态库执行代码,红色代表线程,该程序运行时外部模块执行主线程,在加载动态库后,动态库创建子线程。 注意: 在实际测试中,通过dlopen加载动态库后,紧接着通过getchar()函数阻塞主线程,会发现终端打印the main is lazy, do NOT register me!,因此得出结论:在dlopen()加载动态库时,初始化全局变量,动态库的子线程就开始运行了。 若未阻塞主线程并且主线程成功注册回调函数后,子线程中的函数指针func_in_main未执行动态库中的默认回调函数func_in_main_def,而是执行主线程中的func_in_main函数。(func_in_main在子线程为函数指针变量,在主线程中为函数名称) 博主仍有一个困惑,主线程在加载动态库之后与执行注册回调函数前(中间不阻塞),子线程有没有可能调用的是动态库中的默认回调函数func_in_main_def。(博主将子线程延迟改为1ms,仍然是执行的主线程的回调函数!本质是探究关于子线程启动顺序的问题。) 3.2 动态库动态库中func_in_main和sub_thread都是全局变量,前者用来保存外部模块中的回调函数,以便在动态库的不同地方使用,后者是子线程。 该动态库的目的是创建一个线程,定期调用外部(主程序)注册的函数。默认情况下,线程将调用func_in_main_def,但主程序可以通过调用register_func来注册自己的函数。func_in_lib函数也可以被主程序调用,以便在动态库内部执行注册的外部函数。 动态库被调用时会创建一个子线程,该子线程每隔1000ms循环执行。若外部函数注册回调函数后,该子线程就会将默认的函数替换为外部的回调函数进行执行。 外部函数也可以通过特定的函数,手动调用动态库中得函数func_in_lib进行回调函数处理。 3.3 外部模块外部模块通过typedef定义了多种类型函数指针: typedef void (*pf_void)(void); typedef int (*pfunc)(int); typedef int (*pregister)(pf_void);然后通过dlopen、dlsym等函数加载动态库,并通过register_func函数向动态库注册自己的回调函数func_in_main,然后利用func_in_lib来处理自己的回调函数。 参考: 干货 | 在动态库中如何调用外部函数? https://www.eet-china.com/mp/a51759.html |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |